#ifndef __NBTTAGBASE__
#define __NBTTAGBASE__

#ifndef byte
typedef unsigned char byte;
#endif

#include "NBTTagType.h"
#include "NBTTypeException.h"
#include "..\..\..\Paril\Utils\String.h"

#include <sstream>
#include <queue>

class NBTTagBase
{
	/// <summary>
	/// Tag string values as usually represented in text.
	/// </summary>
	static const char *GetTagString(const int &index)
	{
		static const char *TagStrings[] =
		{
			"TAG_End",
			"TAG_Byte",
			"TAG_Short",
			"TAG_Int",
			"TAG_Long",
			"TAG_Float",
			"TAG_Double",
			"TAG_Byte_Array",
			"TAG_String",
			"TAG_List",
			"TAG_Compound",
			"TAG_Int_Array"
		}; 

		return TagStrings[index];
	}

public:
	/// <summary>
	/// The type that this tag represents.
	/// </summary>
	NBTType Type;

	/// <summary>
	/// The name, if any, of this tag.
	/// </summary>
	std::string Name;

	/// <summary>
	/// The parent tag of this tag.
	/// </summary>
	NBTTagBase *Parent;

	NBTTagBase() :
		Type(TAG_End),
		Name(),
		Parent(nullptr)
	{
	}

	virtual ~NBTTagBase()
	{
	}

	virtual std::string ValueAsString() const { return "???"; };

	virtual NBTType ChildrenType() const { throw std::exception(); }

	/// <summary>
	/// Retrieve a textual representation of the tag.
	/// </summary>
	/// <returns>A textual representation of this tag.</returns>
	virtual std::string ToString() const
	{
		std::stringstream str;

		if (!Name.empty())
			str << Name << " [" << GetTagString(Type) << "] " << ValueAsString();
		else
			str << ValueAsString();

		return str.str();
	}

	template<typename T, typename C>
	static T &popAndReturn(std::queue<T, C> &queue)
	{
		auto &front = queue.front();
		queue.pop();
		return front;
	}

	/// <summary>
	/// Find a child tag that matches this name. This method supports
	/// chained nodes, separated by commas.
	/// </summary>
	/// <example>
	/// Find a single child node: Find("Root")
	/// Find a chained sub-node: Find("Root.Inventory");
	/// </example>
	/// <param name="name">The name or name chain to find.</param>
	/// <returns>The tag it found, else null.</returns>
	const NBTTagBase *Find(const std::string &name) const;

	/// <summary>
	/// Convert this instance to another tag type.
	/// </summary>
	/// <returns>The tag pointer casted.</returns>
	template<typename T>
	const T *As() const
	{
		return static_cast<const T*>(this);
	}

	inline const byte &AsUint8() const;
};

#include "NBTTagValue.h"
#include "NBTTagList.h"
#include "NBTTagCompound.h"

inline const byte &NBTTagBase::AsUint8() const
{
	return As<NBTTagValue<byte>>()->Value;
}

#else
class NBTTagBase;
#endif